/*
 * @(#)CoursesTableModel.java	11/06/02
 * Copyleft 2011. All rights reversed.
 *
 * This file is part of AverageGrade.
 *
 * AverageGrade is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AverageGrade is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with AverageGrade. If not, see <http://www.gnu.org/licenses/>.
 */

package net.grade.averagegrade.swing.table;

import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import net.grade.averagegrade.resource.Messages;

/**
 * A customized table model, intended as a table model in a table displaying
 * information about courses. Has the ability to add a row where all columns
 * are editable. Also comes with the two properties 'isEmpty' and 'isModified',
 * which can be set and checked manually.
 */
public class CoursesTableModel extends DefaultTableModel {

    /** The classes for the respective columns; matching column indexes. */
    private static final Class[] columnClasses = new Class[] { Boolean.class, String.class,
            String.class, Double.class, Integer.class, Date.class };

    /** Contains the model row indexes of the rows set as editable. */
    private final Set<Integer> editableRows = new HashSet<Integer>();

    /** Holds the value of the empty status property. */
    private boolean isEmpty = true;

    /** Holds the value of the modified status property. */
    private boolean isModified;

    /**
     * Creates a new instance of <code>CoursesTableModel</code> using
     * the default settings of the model.
     */
    public CoursesTableModel() {
        super(new Object[][] { { false, null, null, null, null, null } }, new String[] { "",
                Messages.get("CourseCode"), Messages.get("CourseName"), Messages.get("Points"),
                Messages.get("Grade"), Messages.get("Date") });

        /* Update row indexes when an editable row gets deleted from the model. */
        addTableModelListener(new TableModelListener() {
            public void tableChanged(final TableModelEvent e) {
                if(e.getType() == TableModelEvent.DELETE) {
                    final int affectedRows = e.getLastRow() - e.getFirstRow() + 1;
                    for(int i = e.getFirstRow(); i <= e.getLastRow(); i++)
                        editableRows.remove(i);

                    final Set<Integer> newRows = new HashSet<Integer>(editableRows.size());
                    for(Iterator<Integer> it = editableRows.iterator(); it.hasNext(); ) {
                        final int currentRow = it.next();
                        if(currentRow > e.getFirstRow()) {
                            newRows.add(currentRow - affectedRows);
                            it.remove();
                        }
                    }

                    editableRows.addAll(newRows);
                }
            }
        });
    }

    @Override
    public Class getColumnClass(int columnIndex) {
        return columnClasses[columnIndex];
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return !isEmpty && (columnIndex == 0 || columnIndex == 4 || editableRows.contains(rowIndex));
    }

    /**
     * Adds a new editable row using the specified row data.
     *
     * @param rowData The row data for the new editable row.
     */
    public void addEditableRow(final Object[] rowData) {
        super.addRow(rowData);
        editableRows.add(getRowCount() - 1);
    }

    /**
     * Returns <tt>true</tt> if this model has been marked as
     * empty; otherwise <tt>false</tt>.
     *
     * @return <tt>true</tt> if the model has been marked as
     *         empty; otherwise <tt>false</tt>.
     */
    public boolean isEmpty() {
        return isEmpty;
    }

    /**
     * Sets the empty status property of this model.
     *
     * @param empty The value for the empty status property.
     */
    public void setEmpty(final boolean empty) {
        isEmpty = empty;
    }

    /**
     * Returns <tt>true</tt> if this model has been marked as
     * modified; otherwise <tt>false</tt>.
     *
     * @return <tt>true</tt> if the model has been marked as
     *         modified; otherwise <tt>false</tt>.
     */
    public boolean isModified() {
        return isModified;
    }

    /**
     * Sets the modified status property of this model.
     *
     * @param modified The value for the modified status property.
     */
    public void setModified(final boolean modified) {
        isModified = modified;
    }

    /**
     * Checks if the specified model row index is set as an
     * editable row. Returns <tt>true</tt> if the row is set
     * as an editable row.
     *
     * @param rowIndex The model row index of the row to check.
     * @return <tt>true</tt> if the row with the specified row index
     *         is set as an editable row; otherwise <tt>false</tt>.
     */
    public boolean isEditableRow(final int rowIndex) {
        return editableRows.contains(rowIndex);
    }

    /**
     * Returns <tt>true</tt> if the model contains rows
     * marked as editable.
     *
     * @return <tt>true</tt> if the model contains editable rows;
     *         otherwise <tt>false</tt>.
     */
    public boolean hasEditableRows() {
        return (editableRows.size() > 0);
    }

    /**
     * Returns <tt>true</tt> if the model contains normal
     * (i.e. not marked as editable) rows.
     *
     * @return <tt>true</tt> if the model contains normal rows;
     *         otherwise <tt>false</tt>.
     */
    public boolean hasNormalRows() {
        return (getRowCount() > editableRows.size());
    }
}